﻿using Abp.Application.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Abp.Application.Services.Dto;
using Abp.AutoMapper;
using Abp.Domain.Repositories;
using Abp.IdentityFramework;
using Abp.Localization;
using Abp.Runtime.Session;
using Abp.Web.Models;
using KelonProud.Administration.Domain.Authorization;
using KelonProud.Administration.Dtos;
using KelonProud.Administration.Dtos.SearchDto;
using KelonProud.Administration.Managers.Authorization;
using KelonProud.Mvc;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;

namespace KelonProud.Administration.Services
{
    public interface IUserAppService : IAsyncCrudAppService<UserOutputDto, long, PagedResultRequestDto, UserInputDto, UserOutputDto>
    {
        Task<ListResultDto<RoleOutPutDto>> GetRoles();

        UserOutputDto GetUserInfo(string userAccount);

        UserOutputDto GetCurrentUserInfo();

        Task<UserSearchOutput> GetUsers(UserSearchInput searchInput);
        Task<UserSearchOutput> GetOrganizationUsers(OrgUnitUserSearchInput searchInput);
        Task<UserSearchOutput> GetChoseUsers(OrgUnitUserSearchInput searchInput);

        void SetUserOrganization(int orgUnitId, List<int> userIds);
        void RemoveOrganization(List<int> userIds);

        void ActiveUserByIds(string ids);

        void DeleteUserByIds(string ids);

        void ResetPwdByIds(string ids);

        Task ChangePassword(UserModifyPwdInPut inPut);
    }

    public class UserAppService : KelonProudAsyncCrudAppService<User, UserOutputDto, long, PagedResultRequestDto, UserInputDto, UserOutputDto>, IUserAppService
    {
        private readonly UserManager _userManager;
        private readonly RoleManager _roleManager;
        private readonly IRepository<Role> _roleRepository;
        private readonly IPasswordHasher<User> _passwordHasher;

        public UserAppService(
            IRepository<User, long> repository,
            UserManager userManager,
            RoleManager roleManager,
            IRepository<Role> roleRepository,
            IPasswordHasher<User> passwordHasher)
            : base(repository)
        {
            _userManager = userManager;
            _roleManager = roleManager;
            _roleRepository = roleRepository;
            _passwordHasher = passwordHasher;
        }

        public override async Task<UserOutputDto> Save(UserInputDto input)
        {
            var user = _userManager.GetById(input.Id.ToInt32()) ?? new User();

            user.UserName = input.UserName;
            user.Name = input.Name;
            user.Surname = input.Name;
            user.PhoneNumber = input.PhoneNumber;
            user.EmailAddress = input.EmailAddress;
            user.TenantId = input.TenantId;
            user.IsLockoutEnabled = input.IsLockoutEnabled;
            user.OrganizationUnitId = input.OrganizationUnitId;

            if (user.Id == 0)
            {
                user.SetNormalizedNames();
                user.Id  = _userManager.CreateUser(user);
                user.Password = _passwordHasher.HashPassword(user, User.DefaultPassword);
            }
            else
            {
                await _userManager.UpdateAsync(user);
            }

            CurrentUnitOfWork.SaveChanges();
          
            await _userManager.SetRoles(user.Id, input.RoleIds);

            return user.MapTo<UserOutputDto>();
        }

        public override async Task<UserOutputDto> Create(UserInputDto input)
        {
            CheckCreatePermission();

            var user = ObjectMapper.Map<User>(input);

            user.TenantId = AbpSession.TenantId;
            user.Password = User.DefaultPassword; 
            user.IsEmailConfirmed = true;

            CheckErrors(await _userManager.CreateAsync(user));

            if (input.RoleNames != null)
            {
                CheckErrors(await _userManager.SetRoles(user, input.RoleNames));
            }

            CurrentUnitOfWork.SaveChanges();

            return MapToEntityDto(user);
        }
    
        public override async Task Delete(EntityDto<long> input)
        {
            var user = await _userManager.GetUserByIdAsync(input.Id);
            await _userManager.DeleteAsync(user);
        }

        public async Task<ListResultDto<RoleOutPutDto>> GetRoles()
        {
            var roles = await _roleRepository.GetAllListAsync();
            return new ListResultDto<RoleOutPutDto>(ObjectMapper.Map<List<RoleOutPutDto>>(roles));
        }

        public UserOutputDto GetUserInfo(string userAccount)
        {
            var user = (_userManager.Users.FirstOrDefault(f => f.UserName == userAccount) ??
                        _userManager.Users.FirstOrDefault(f => f.EmailAddress == userAccount)) ??
                       _userManager.Users.FirstOrDefault(f => f.PhoneNumber == userAccount);

            return user?.MapTo<UserOutputDto>();
        }

        public UserOutputDto GetCurrentUserInfo()
        {
            return _userManager.CurretUserOutPut.MapTo<UserOutputDto>();
        }

        [WrapResult(WrapOnSuccess = false)]
        public Task<UserSearchOutput> GetUsers(UserSearchInput searchInput)
        {
            var result = new UserSearchOutput();
            var query = _userManager.UserStore.Users;

            if (searchInput.Name.HasValue())
            {
                query = query.Where(w => w.Name.Contains(searchInput.Name));
            }

            if (searchInput.UserName.HasValue())
            {
                query = query.Where(w => w.UserName.Contains(searchInput.UserName));
            }

            if (searchInput.EmailAddress.HasValue())
            {
                query = query.Where(w => w.UserName.Contains(searchInput.EmailAddress));
            }

            if (searchInput.PhoneNumber.HasValue())
            {
                query = query.Where(w => w.UserName.Contains(searchInput.PhoneNumber));
            }

            result.Total = query.Count();
            result.Rows = query.Skip(searchInput.SkipCount).Take(searchInput.SearchCount).ToList()
                .Select(s => s.MapTo<UserOutputDto>()).ToList();

            return Task.FromResult(result);
        }

        [WrapResult(WrapOnSuccess = false)]
        public Task<UserSearchOutput> GetOrganizationUsers(OrgUnitUserSearchInput searchInput)
        {
            var result = new UserSearchOutput();
            var query = _userManager.UserStore.Users.Where(w=>w.OrganizationUnitId==searchInput.OrgUnitId);

            result.Total = query.Count();
            result.Rows = query.Skip(searchInput.SkipCount).Take(searchInput.SearchCount).ToList()
                .Select(s => s.MapTo<UserOutputDto>()).ToList();

            return Task.FromResult(result);
        }

        [WrapResult(WrapOnSuccess = false)]
        public Task<UserSearchOutput> GetChoseUsers(OrgUnitUserSearchInput searchInput)
        {
            var result = new UserSearchOutput();
            var query = _userManager.UserStore.Users.Where(w => w.OrganizationUnitId == null);

            if (searchInput.SearchStr.HasValue())
            {
                query = query.Where(w => w.Name.Contains(searchInput.SearchStr) || w.UserName.Contains(searchInput.SearchStr));
            }

            result.Total = query.Count();
            result.Rows = query.Skip(searchInput.SkipCount).Take(searchInput.SearchCount).ToList()
                .Select(s => s.MapTo<UserOutputDto>()).ToList();

            return Task.FromResult(result);
        }

        public void SetUserOrganization(int orgUnitId, List<int> userIds)
        {
            userIds.Each(e =>
            {
                var user = _userManager.GetById(e);

                if (user != null)
                {
                    user.OrganizationUnitId = e;
                    _userManager.UpdateUser(user);
                }
            });
        }

        public void RemoveOrganization(List<int> userIds)
        {
            userIds.Each(e =>
            {
                var user = _userManager.GetById(e);

                if (user != null)
                {
                    user.OrganizationUnitId = null;
                    _userManager.UpdateUser(user);
                }
            });
        }

        public void ActiveUserByIds(string ids)
        {
            if (ids.HasValue())
            {
                var idList = ids.Split(',');

                idList.Each(e =>
                {
                    var user = _userManager.GetById(e.ToInt32());
                    if (user != null)
                    { 
                        user.IsActive = !user.IsActive;
                        _userManager.UpdateUser(user);
                    }
                });
            }
        }

        public void DeleteUserByIds(string ids)
        {
            if (ids.HasValue())
            {
                var idList = ids.Split(',');

                idList.Each(e =>
                {
                    var user = _userManager.GetById(e.ToInt32());
                    if (user != null)
                    {
                        user.IsDeleted = !user.IsDeleted;
                        _userManager.UpdateUser(user);
                    }
                });
            }
        }

        public void ResetPwdByIds(string ids)
        {
            if (ids.HasValue())
            {
                var idList = ids.Split(',');

                foreach (var id in idList)
                {
                    var user = _userManager.GetById(id.ToInt32());
                    if (user != null)
                    {
                        user.Password = _userManager.PasswordHasher.HashPassword(user, User.DefaultPassword);
                        _userManager.UpdateUser(user);
                    }
                }
            }
        }

        public Task ChangePassword(UserModifyPwdInPut inPut)
        {
            var user = _userManager.CurrentUser;

            if (_userManager.PasswordHasher.VerifyHashedPassword(user, user.Password, inPut.OldPassWord) ==
                PasswordVerificationResult.Failed)
            {
                throw new Exception("原密码错误");
            }

            if (inPut.NewPassWord != inPut.NewPassWordR)
            {
                throw new Exception("两次输入新密码不一致");
            }

            user.Password = _userManager.PasswordHasher.HashPassword(user, inPut.NewPassWord);
            _userManager.UpdateUser(user);

            return  Task.FromResult("");
        }

        protected override User MapToEntity(UserInputDto createInput)
        {
            var user = ObjectMapper.Map<User>(createInput);
            user.SetNormalizedNames();
            return user;
        }

        protected override void MapToEntity(UserOutputDto input, User user)
        {
            ObjectMapper.Map(input, user);
            user.SetNormalizedNames();
        }

        protected override UserOutputDto MapToEntityDto(User user)
        {
            var roles = _roleManager.Roles.Where(r => user.Roles.Any(ur => ur.RoleId == r.Id)).Select(r => r.NormalizedName);
            var userDto = base.MapToEntityDto(user);
         //   userDto.RoleNames = roles.ToArray();
            return userDto;
        }

        protected override IQueryable<User> CreateFilteredQuery(PagedResultRequestDto input)
        {
            return Repository.GetAllIncluding(x => x.Roles);
        }

        protected override async Task<User> GetEntityByIdAsync(long id)
        {
            return await Repository.GetAllIncluding(x => x.Roles).FirstOrDefaultAsync(x => x.Id == id);
        }

        protected override IQueryable<User> ApplySorting(IQueryable<User> query, PagedResultRequestDto input)
        {
            return query.OrderBy(r => r.UserName);
        }

        protected virtual void CheckErrors(IdentityResult identityResult)
        {
            identityResult.CheckErrors(LocalizationManager);
        }
    }
}
